﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Reflection;

namespace BlueMarble.Genesis.Client
{
    static class Program
    {
        static System.Data.DataSet globalDataSet;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            globalDataSet = new System.Data.DataSet();
            global::Genesis.Common.Classes.GlobalDataSetFunctions globalDataSetFunctions = new global::Genesis.Common.Classes.GlobalDataSetFunctions(globalDataSet);

            global::Genesis.Client.Host.Interfaces.ISplashForm splashForm = new BlueMarble.Genesis.Client.Standard.Forms.Splash();
            global::Genesis.Client.Host.Interfaces.ILogin login = new BlueMarble.Genesis.Client.Standard.Security.Authentication();

            if (login.AuthenticateUser(globalDataSet))
            {
                splashForm.RefreshWindow();
                splashForm.ShowSplashForm();

                Guid _sessionGuid = globalDataSetFunctions.GetActiveSessionGuid();

                // Set properties in global data set
                globalDataSetFunctions.SetProperty("SessionGuid", _sessionGuid.ToString());
                globalDataSetFunctions.SetProperty("UserGuid", ((Guid)globalDataSet.Tables["Active_Application"].Rows[0]["UserGuid"]).ToString());

                globalDataSetFunctions.SetProperty("ApplicationPath", AppDomain.CurrentDomain.BaseDirectory);
                globalDataSetFunctions.SetProperty("GenesisServerAddress", System.Configuration.ConfigurationManager.AppSettings["GenesisServerAddress"]);

                // Process Meta-Data
                // Get the module information from the server
                global::Genesis.Client.Module.Classes.ModuleProvider moduleProvider = new global::Genesis.Client.Module.Classes.ModuleProvider();

                moduleProvider.SessionGuid = _sessionGuid;

                globalDataSetFunctions.MergeTables("Module_File", moduleProvider.GetApplicationFiles(globalDataSetFunctions.GetActiveApplicationGuid()));

                System.Xml.XmlDocument localFiles = new System.Xml.XmlDocument();

                // Load or create the local file cache
                if (System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "\\filecache.xml"))
                {
                    splashForm.UpdateSplashStatus("Reading file cache");
                    localFiles.Load(AppDomain.CurrentDomain.BaseDirectory + "\\filecache.xml");
                }
                else
                {
                    splashForm.UpdateSplashStatus("Creating file cache");
                    localFiles.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8"" ?><files></files>");
                }

                // Process application file information
                foreach (System.Data.DataRow dr in globalDataSet.Tables["Module_File"].Rows)
                {
                    bool createEntry = false;
                    bool downloadFile = false;

                    string filename = (string)dr["FileName"];
                    string fileUrl = (string)dr["FileUrl"];
                    string fileDescription = null;
                    string fileVersion = (string)dr["FileVersion"];

                    if (dr["FileDescription"] != DBNull.Value)
                    {
                        fileDescription = (string)dr["FileDescription"];
                    }

                    splashForm.UpdateSplashStatus("Loading " + filename);
                    splashForm.UpdateSplashStatus("\tOnline Version: " + fileVersion);

                    System.Xml.XmlNode fn = localFiles.DocumentElement.SelectSingleNode(string.Format("/files/file[@name='{0}']", filename));

                    if (fn == null)
                    {
                        splashForm.UpdateSplashStatus("\tNo local version found");
                        createEntry = true;
                        downloadFile = true;
                    }
                    else
                    {
                        splashForm.UpdateSplashStatus("\tLocal Version: " + fn.Attributes["version"].Value);
                        if (fileVersion != fn.Attributes["version"].Value)
                        {
                            downloadFile = true;
                            fn.Attributes["version"].Value = fileVersion;
                        }
                    }

                    if (downloadFile)
                    {
                        splashForm.UpdateSplashStatus("\tDownloading file");
                        splashForm.UpdateSplashStatus("Downloading " + ((fileDescription == null) ? filename : fileDescription) + "...");

                        DownloadFile(filename, fileUrl, splashForm);
                    }

                    if (createEntry)
                    {
                        splashForm.UpdateSplashStatus("Creating file cache entry");
                        System.Xml.XmlNode fileNode = localFiles.CreateElement("file");

                        System.Xml.XmlAttribute nameAttribute = localFiles.CreateAttribute("name");
                        nameAttribute.Value = filename;
                        System.Xml.XmlAttribute urlAttribute = localFiles.CreateAttribute("url");
                        urlAttribute.Value = System.Web.HttpUtility.UrlEncode(fileUrl);
                        System.Xml.XmlAttribute versionAttribute = localFiles.CreateAttribute("version");
                        versionAttribute.Value = fileVersion;

                        fileNode.Attributes.Append(nameAttribute);
                        fileNode.Attributes.Append(urlAttribute);
                        fileNode.Attributes.Append(versionAttribute);

                        localFiles.DocumentElement.AppendChild(fileNode);
                    }

                    globalDataSetFunctions.MergeTables("Module_Command", moduleProvider.GetApplicationCommands((Guid)dr["FileGuid"]));
                }

                splashForm.UpdateSplashStatus("Saving file cache");
                localFiles.Save(AppDomain.CurrentDomain.BaseDirectory + "\\filecache.xml");
                
                splashForm.UpdateSplashStatus("Reading application configuration...");

                globalDataSetFunctions.MergeTables("Module_Ribbon", moduleProvider.GetApplicationRibbons());

                foreach (System.Data.DataRow dr in globalDataSet.Tables["Module_Ribbon"].Rows)
                {
                    globalDataSetFunctions.MergeTables("Module_RibbonBar", moduleProvider.GetRibbonBars((Guid)dr["RibbonGuid"]));
                }

                if (globalDataSet.Tables["Module_RibbonBar"] != null)
                {
                    foreach (System.Data.DataRow dr in globalDataSet.Tables["Module_RibbonBar"].Rows)
                    {
                        GetBarItemsRecursive(globalDataSetFunctions, moduleProvider, (Guid)dr["RibbonBarGuid"], Guid.Empty);
                    }
                }

                // Create the main form
                splashForm.UpdateSplashStatus("Creating host form...");
                BlueMarble.Genesis.Client.Standard.Forms.Main mainForm = new BlueMarble.Genesis.Client.Standard.Forms.Main();
                global::Genesis.Common.Interfaces.IHost mainFormIHost = (global::Genesis.Common.Interfaces.IHost)mainForm;
                global::Genesis.Common.Interfaces.IDataContainer mainFormDataContainer = (global::Genesis.Common.Interfaces.IDataContainer)mainForm;
                
                mainFormDataContainer.GlobalDataSet = globalDataSet;
                mainForm.Text = globalDataSetFunctions.GetActiveApplicationName();

                System.Data.DataRow[] commands = null;

                if (globalDataSet.Tables["Module_Command"] != null)
                {
                    commands = globalDataSet.Tables["Module_Command"].Select("1=1", "FileOrder ASC");
                }

                if (commands != null)
                {
                    foreach (System.Data.DataRow dr in commands)
                    {
                        try
                        {
                            global::Genesis.Common.Interfaces.ICommandInfo command = new global::Genesis.Common.Classes.CommandInfoBase();

                            command.Host = mainForm;
                            command.Guid = (Guid)dr["CommandGuid"];
                            command.Id = (int)dr["CommandId"];
                            command.FileGuid = (Guid)dr["FileGuid"];
                            command.Name = (string)dr["CommandName"];
                            command.Code = (string)dr["CommandCode"];

                            string filename = (string)globalDataSet.Tables["Module_File"].Select(string.Format("FileGuid = '{0}'", dr["FileGuid"]))[0]["FileName"];

                            filename = AppDomain.CurrentDomain.BaseDirectory + "\\" + filename;
                            System.Diagnostics.Debug.WriteLine("FILE:" + filename);

                            splashForm.UpdateSplashStatus("Loading command " + command.Name);

                            System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFile(filename);

                            Type[] types = assembly.GetExportedTypes();

                            foreach (Type type in types)
                            {
                                string typeName = (string)dr["CommandType"];

                                if (type.FullName == typeName)
                                {
                                    Type commandInterface = type.GetInterface("Genesis.Common.Interfaces.ICommand");

                                    if (commandInterface != null)
                                    {
                                        command.Command = (global::Genesis.Common.Interfaces.ICommand)type.InvokeMember(null,
                                            BindingFlags.DeclaredOnly |
                                            BindingFlags.Public | BindingFlags.NonPublic |
                                            BindingFlags.Instance | BindingFlags.CreateInstance,
                                            null, null, null);

                                        command.Command.Host = mainFormIHost;
                                    }
                                }
                                else
                                {
                                    Type globalDataSetFunctionsInterface = type.GetInterface("Genesis.Common.Interfaces.IGlobalDataSetFunctionsInitializer");

                                    if (globalDataSetFunctionsInterface != null)
                                    {
                                        global::Genesis.Common.Interfaces.IGlobalDataSetFunctionsInitializer iGlobalDataSetFunctionsInitializer =
                                            (global::Genesis.Common.Interfaces.IGlobalDataSetFunctionsInitializer)type.InvokeMember(null,
                                                BindingFlags.DeclaredOnly |
                                                BindingFlags.Public | BindingFlags.NonPublic |
                                                BindingFlags.Instance | BindingFlags.CreateInstance,
                                                null, null, null);

                                        iGlobalDataSetFunctionsInitializer.Initialize(globalDataSetFunctions);
                                    }
                                }
                            }

                            mainFormIHost.Commands.Add(command);
                        }
                        catch (System.Exception ex)
                        {
                            throw ex;
                        }
                    }
                }

                mainFormIHost.SynchronizeApplicationRibbons();

                splashForm.UpdateSplashStatus("Executing startup script...");
                mainForm.ExecuteStartupScript();

                splashForm.HideSplashForm();

                Application.Run(mainForm);
            }
            else
            {
                Application.Exit();
            }
        }


        public static void DownloadFile(string FileName, string FileUrl, global::Genesis.Client.Host.Interfaces.ISplashForm SplashForm)
        {
            DialogResult retryResult = DialogResult.Retry;

            while (retryResult == DialogResult.Retry)
            {
                try
                {
                    if (!System.IO.Directory.Exists(AppDomain.CurrentDomain.BaseDirectory + "\\"))
                    {
                        System.IO.Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + "\\");
                    }

                    System.Net.WebRequest webRequest = System.Net.WebRequest.CreateDefault(new Uri(FileUrl + "?CacheKiller=" + Guid.NewGuid().ToString()));

                    int retryCount = 0;
                    bool downloaded = false;
                    System.IO.FileStream streamWriter = null;
                    System.IO.Stream fileStream = null;

                    while (!downloaded)
                    {
                        try
                        {
                            fileStream = webRequest.GetResponse().GetResponseStream();

                            // 20100424 Stephan Johnson - Removed this code because the local file is not being referenced with a 'x' suffix
                            //if ((FileUrl.ToLower().EndsWith("x")) &&
                            //    (!FileName.ToLower().EndsWith("x")))
                            //{
                            //    FileName += "x";
                            //}

                            streamWriter = System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "\\" + FileName);

                            downloaded = true;
                        }
                        catch
                        {
                            retryCount++;

                            if (retryCount >= 2)
                            {
                                downloaded = true;

                                retryResult = DialogResult.OK;

                                break;
                            }
                        }
                    }

                    if ((streamWriter != null) && (fileStream != null))
                    {
                        int value = fileStream.ReadByte();
                        int counter = 0;

                        while (value != -1)
                        {
                            streamWriter.WriteByte((byte)value);

                            value = fileStream.ReadByte();
                            counter++;
                        }

                        streamWriter.Close();

                        retryResult = DialogResult.OK;
                    }
                }
                catch
                {
                    throw;
                }
            }
        }

        public static void GetBarItemsRecursive(global::Genesis.Common.Classes.GlobalDataSetFunctions GlobalDataSetFunctions, global::Genesis.Client.Module.Classes.ModuleProvider ModuleProvider, Guid RibbonBarGuid, Guid ParentBarItemGuid)
        {
            try
            {
                System.Data.DataTable barItemTable = ModuleProvider.GetBarItems(RibbonBarGuid, ParentBarItemGuid);

                GlobalDataSetFunctions.MergeTables("Module_BarItem", barItemTable);

                System.Data.DataRow[] barItems = barItemTable.Select("1=1");

                foreach (System.Data.DataRow dr in barItems)
                {
                    GetBarItemsRecursive(GlobalDataSetFunctions, ModuleProvider, (Guid)dr["RibbonBarGuid"], (Guid)dr["BarItemGuid"]);
                }
            }
            catch
            {
                throw;
            }
        }
    }
}
